<?php
/**
 * Pageley_Test_Case is an abstract base class for all tests. It provides several assertion functions
 * to assist evaluating test results. All methods starting with "test" will be executed as a test. If the method
 * throws Pageley_Test_AssertionException, the test will be marked as failed. If the method throws any
 * other exception, it will be marked as error. If the method does not throw an exception, it will be marked as
 * successful. You can override the methods setUp() and tearDown() to perform some common logic before and
 * after executing a test.
 *
 * @package Pageley.Test
 * @version $Revision: 1.1 $
 *
 * @author Stijn Van Bael
 * @since 1.0
 */
abstract class Pageley_Test_Case {

	/**
	 * Performs common logic before every test. Override this method to add logic.
	 */
	protected function setUp() { }

	/**
	 * Performs common logic after every test. Override this method to add logic.
	 */
	protected function tearDown() { }
	
	/**
	 * Typically does some cleanup work when a test fails.
	 */
	protected function onFail() { }

	/**
	 * Returns the name of this test.
	 * @return string the name of this test.
	 */
	public function getName() {
		$reflector = new ReflectionObject($this);
		return $reflector->getName();
	}

	/**
	 * Executes all tests.
	 * @return Pageley_Test_CaseResult the results of this case.
	 */
	public function run() {
		$reflector = new ReflectionObject($this);
		$caseResult = new Pageley_Test_CaseResult($this->getName());
		foreach($reflector->getMethods() as $method) {
			$methodName = $method->getName();
			if(Pageley_Util_String::startsWith($methodName, "test")) {
				try {
					Pageley_Log::info($this, "Running test " . get_class($this) . "." . $methodName . ".");
					$start = new Pageley_Util_Timestamp();
					$this->setUp();
					$method->invoke($this);
					$this->tearDown();
					$caseResult->addTestResult(new Pageley_Test_Result($methodName));
					$end = new Pageley_Util_Timestamp();
					$duration = $end->getTime() - $start->getTime();
                    Pageley_Log::info($this, "Test " . get_class($this) . "." . $methodName . " completed successfully in " . $duration . "ms.");
				} catch(Pageley_Test_AssertionException $exception) {
					Pageley_Log::error($this, "Test " . get_class($this) . "." . $methodName . " failed!");
					$caseResult->addTestResult(new Pageley_Test_Result($methodName, Pageley_Test_ResultType::FAILURE, $exception));
				} catch(Exception $exception) {
					Pageley_Log::error($this, "Test " . get_class($this) . "." . $methodName . " failed!.");
					$caseResult->addTestResult(new Pageley_Test_Result($methodName, Pageley_Test_ResultType::ERROR, $exception));
				}
			}
		}
		return $caseResult;
	}

	/**
	 * Verifies whether or not two values are equal.
	 * @param mixed expected the expected value
	 * @param mixed value the value to test
	 * @param string message a message to show when expected and value are not equal
	 * @throws Pageley_Test_AssertionException when expected and value are not equal
	 */
	protected function assertEquals($expected, $value, $message = null) {
		if(!Pageley_Util_Equals::isEqual($expected, $value)) {
			$this->fail("Expected <" . $expected . ">, found <" . ($value === null ? "null" : $value) . ">. " . $message);
		}
	}

	/**
	 * Verifies whether or not two values are not equal.
	 * @param mixed expected the expected value
	 * @param mixed value the value to test
	 * @param string message a message to show when expected and value are equal
	 * @throws Pageley_Test_AssertionException when expected and value are equal
	 */
	protected function assertNotEquals($expected, $value, $message = null) {
		if(Pageley_Util_Equals::isEqual($expected, $value)) {
			$this->fail("Did not expect <" . $expected . ">. " . $message);
		}
	}

	/**
	 * Verifies whether or not the provided value is true.
	 * @param boolean value the value to test
	 * @param string message a message to show when the value is not true
	 * @throws Pageley_Test_AssertionException when the value is not true
	 */
	protected function assertTrue($value, $message = null) {
		if(!$value) {
			$this->fail("Expected true. " . $message);
		}
	}

	/**
	 * Verifies whether or not the provided value is false.
	 * @param boolean value the value to test
	 * @param string message a message to show when the value is not false
	 * @throws Pageley_Test_AssertionException when the value is not false
	 */
	protected function assertFalse($value, $message = null) {
		if($value) {
			$this->fail("Expected false. " . $message);
		}
	}

	/**
	 * Verifies whether or not two values are identical (share the same memory location).
	 * @param mixed expected the expected value
	 * @param mixed value the value to test
	 * @param string message a message to show when expected and value are not identical
	 * @throws Pageley_Test_AssertionException when expected and value are not identical
	 */
	protected function assertSame($expected, $value, $message = null) {
		if($expected !== $value) {
			$this->fail("Expected <" . $expected . ">, found <" . ($value === null ? "null" : $value) . "> as a different instance. " . $message);
		}
	}

	/**
	 * Verifies whether or not two values are not identical (share the same memory location).
	 * @param mixed expected the expected value
	 * @param mixed value the value to test
	 * @param string message a message to show when expected and value are identical
	 * @throws Pageley_Test_AssertionException when expected and value are identical
	 */
	protected function assertNotSame($expected, $value, $message = null) {
		if($expected === $value) {
			$this->fail("Did not expect <" . $expected . ">. " . $message);
		}
	}

	/**
	 * Verifies whether or not the provided value is null.
	 * @param mixed value the value to test
	 * @param string message a message to show when the value is not null
	 * @throws Pageley_Test_AssertionException when the value is not null
	 */
	protected function assertNull($value, $message = null) {
		if(null !== $value) {
			$this->fail("Expected null, found <" . $expected . ">. " . $message);
		}
	}

	/**
	 * Verifies whether or not the provided value is not null.
	 * @param mixed value the value to test
	 * @param string message a message to show when the value is null
	 * @throws Pageley_Test_AssertionException when the value is null
	 */
	protected function assertNotNull($value, $message = null) {
		if(null === $value) {
			$this->fail("Did not expect null. " . $message);
		}
	}

	/**
	 * Fails the current test with the provided message. Use this for example
	 * after a method which you expect to throw an exception. If the exception
	 * is not thrown, the test fails.
	 * @param string message a message to show
	 * @throws Pageley_Test_AssertionException always
	 */
	protected function fail($message) {
		$this->onFail();
		throw new Pageley_Test_AssertionException($message);
	}
}